/*************************************************************************************
 *  Copyright (C) 2007-2008 by Aleix Pol <aleixpol@gmail.com>                        *
 *                                                                                   *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                   *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                   *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "operatorsmodel.h"
#include "analitza/operator.h"
#include "analitza/variables.h"
#include <QApplication>
#include <QFont>

OperatorsModel::OperatorsModel(QObject *parent) : QAbstractTableModel(parent), m_vars(0)
{
}

QVariant OperatorsModel::data(const QModelIndex & index, int role) const
{
	QVariant ret;
	if(role==Qt::DisplayRole) {
		if(index.row()<Operator::nOfOps-2) {
			Operator oper((Operator::OperatorType) (index.row()+1));
			
			switch(index.column()) {
				case 0:
					ret=oper.toString();
					break;
				case 1:
					ret=description(oper);
					break;
				case 2:
					ret=sample(oper);
					break;
				case 3:
					ret=example(oper);
					break;
			}
		} else if(m_vars) {
			int var=index.row()-Operator::nOfOps+2;
			QString key=m_vars->keys()[var];
			switch(index.column()) {
				case 0:
					ret=key;
					break;
				case 1:
					ret=m_vars->value(key)->toString();
					break;
			}
		}
	} else if(role==Qt::FontRole && index.column()==1) {
                QFont f = QApplication::font();
		f.setItalic(true);
		ret=f;
	}
	return ret;
}

QVariant OperatorsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	QVariant ret;
	if(role==Qt::DisplayRole && orientation==Qt::Horizontal) {
		switch(section) {
			case 0:
                                ret=QString("Name");
				break;
			case 1:
                                ret=QString("Description");
				break;
			case 2:
                                ret=QString("Parameters");
				break;
			case 3:
                                ret=QString("Example");
				break;
		}
	}
	return ret;
}

int OperatorsModel::rowCount(const QModelIndex &) const
{
	int count=Operator::nOfOps;
	if(m_vars)
		count+=m_vars->count();
	return count-2;
}

int OperatorsModel::columnCount(const QModelIndex &) const
{
	return 4;
}

void OperatorsModel::updateInformation()
{
	reset();
}

QString OperatorsModel::sample(Operator oper)
{
	QString funcname=oper.toString();
	QString bounds;
	if(oper.isBounded()) {
		bounds=" : var=from..to";
	}
	
        QString sample = QString("%1(").arg(funcname);
	
	if(oper.nparams()<0) {
                return QString("%1... parameters, ...%2)").arg(sample).arg(bounds);
	} else {
		for(int i=0; i<oper.nparams(); ++i) {
                        sample += QString("par%1").arg(i+1);
			if(i<oper.nparams()-1)
				sample += ", ";
		}
		return sample+bounds+')';
	}
}

QString OperatorsModel::description(Operator o)
{
	QString s;
	switch(o.operatorType()) {
		case Operator::plus:
                        s = QString("Addition");
			break;
		case Operator::times:
                        s = QString("Multiplication");
			break;
		case Operator::divide:
                        s = QString("Division");
			break;
		case Operator::minus:
                        s = QString("Subtraction");
			break;
		case Operator::power:
                        s = QString("Power");
			break;
		case Operator::rem:
                        s = QString("Remainder");
			break;
		case Operator::quotient:
                        s = QString("Quotient");
			break;
		case Operator::factorof:
                        s = QString("The factor of");
			break;
		case Operator::factorial:
                        s = QString("Factorial. factorial(n)=n!");
			break;
		case Operator::sin:
                        s = QString("Function to calculate the sine of a given angle");
			break;
		case Operator::cos:
                        s = QString("Function to calculate the cosine of a given angle");
			break;
		case Operator::tan:
                        s = QString("Function to calculate the tangent of a given angle");
			break;
		case Operator::sec:
                        s = QString("Secant");
			break;
		case Operator::csc:
                        s = QString("Cosecant");
			break;
		case Operator::cot:
                        s = QString("Cotangent");
			break;
		case Operator::sinh:
                        s = QString("Hyperbolic sine");
			break;
		case Operator::cosh:
                        s = QString("Hyperbolic cosine");
			break;
		case Operator::tanh:
                        s = QString("Hyperbolic tangent");
			break;
		case Operator::sech:
                        s = QString("Hyperbolic secant");
			break;
		case Operator::csch:
                        s = QString("Hyperbolic cosecant");
			break;
		case Operator::coth:
                        s = QString("Hyperbolic cotangent");
			break;
		case Operator::arcsin:
                        s = QString("Arc sine");
			break;
		case Operator::arccos:
                        s = QString("Arc cosine");
			break;
		case Operator::arctan:
                        s = QString("Arc tangent");
			break;
		case Operator::arccot:
                        s = QString("Arc cotangent");
			break;
// 		case Operator::arccoth:
// 			s = QString("Hyperbolic arc cotangent");
// 			break;
		case Operator::arctanh:
                        s = QString("Hyperbolic arc tangent");
			break;
		case Operator::sum:
                        s = QString("Summatory");
			break;
		case Operator::product:
                        s = QString("Productory");
			break;
		case Operator::diff:
                        s = QString("Differentiation");
			break;
		case Operator::arcsinh:
                        s = QString("Hyperbolic arc sine");
			break;
		case Operator::arccosh:
                        s = QString("Hyperbolic arc cosine");
			break;
		case Operator::arccsc:
                        s = QString("Arc cosecant");
			break;
		case Operator::arccsch:
                        s = QString("Hyperbolic arc cosecant");
			break;
		case Operator::arcsec:
                        s = QString("Arc secant");
			break;
		case Operator::arcsech:
                        s = QString("Hyperbolic arc secant");
			break;
		case Operator::exp:
                        s = QString("Exponent (e^x)");
			break;
		case Operator::ln:
                        s = QString("Base-e logarithm");
			break;
		case Operator::log:
                        s = QString("Base-10 logarithm");
			break;
		case Operator::abs:
                        s = QString("Absolute value. abs(n)=|n|");
			break;
// 		case Operator::conjugate:
// 			s = QString("Conjugate");
// 			break;
// 		case Operator::arg:
// 			s = "---";//QString("Arg?");
// 			break;
// 		case Operator::real:
// 			s = QString("Real");
// 			break;
// 		case Operator::imaginary:
// 			s = QString("Imaginary");
// 			break;
		case Operator::floor:
                        s = QString("Floor value. floor(n)=⌊n⌋");
			break;
		case Operator::ceiling:
                        s = QString("Ceil value. ceil(n)=⌈n⌉");
			break;
		case Operator::min:
                        s = QString("Minimum");
			break;
		case Operator::max:
                        s = QString("Maximum");
			break;
		case Operator::gt:
                        s = QString("Greater than. gt(a,b)=a>b");
			break;
		case Operator::lt:
                        s = QString("Less than. lt(a,b)=a<b");
			break;
		case Operator::eq:
                        s = QString("Equal. eq(a,b) = a=b");
			break;
		case Operator::approx:
                        s = QString("Approximation. approx(a)=a±n");
			break;
		case Operator::neq:
                        s = QString("Not equal. neq(a,b)=a≠b");
			break;
		case Operator::geq:
                        s = QString("Greater or equal. geq(a,b)=a≥b");
			break;
		case Operator::leq:
                        s = QString("Less or equal. leq(a,b)=a≤b");
			break;
		case Operator::_and:
                        s = QString("Boolean and");
			break;
		case Operator::_not:
                        s = QString("Boolean not");
			break;
		case Operator::_or:
                        s = QString("Boolean or");
			break;
		case Operator::_xor:
                        s = QString("Boolean xor");
			break;
		case Operator::implies:
                        s = QString("Boolean implication");
			break;
		case Operator::gcd:
                        s = QString("Greatest common divisor");
			break;
		case Operator::lcm:
                        s = QString("Least common multiple");
			break;
		case Operator::root:
                        s = QString("Root");
			break;
		case Operator::card:
                        s = QString("Cardinal");
			break;
		case Operator::scalarproduct:
                        s = QString("Scalar product");
			break;
		case Operator::selector:
                        s = QString("Select an element from a container");
			break;
		default:
			break;
	}
	return s;
}

QString OperatorsModel::example(Operator o)
{
	QString s;
	
	switch(o.operatorType()) {
		case Operator::plus:
			s="x+2";
			break;
		case Operator::times:
			s="x*2";
			break;
		case Operator::divide:
			s="x/2";
			break;
		case Operator::minus:
			s="x-2";
			break;
		case Operator::power:
			s="x**2";
			break;
		case Operator::rem:
			s="rem(x, 5)";
			break;
		case Operator::quotient:
			s="quotient(x, 2)";
			break;
		case Operator::factorof:
			s="factorof(x, 3)";
			break;
		case Operator::min:
			s="min(x, 4)";
			break;
		case Operator::max:
			s="max(x, 4)";
			break;
		case Operator::gt:
			s="gt(x, 4)";
			break;
		case Operator::lt:
			s="lt(x, 4)";
			break;
		case Operator::eq:
			s="eq(x, 4)";
			break;
		case Operator::approx:
			s="approx(x, 4)";
			break;
		case Operator::neq:
			s="neq(x, 4)";
			break;
		case Operator::geq:
			s="geq(x, 4)";
			break;
		case Operator::leq:
			s="leq(x, 4)";
			break;
		case Operator::_and:
			s="and(gt(x,-2), lt(x,2))";
			break;
		case Operator::_or:
			s="or(gt(x,2), lt(x,-2))";
			break;
		case Operator::_xor:
			s="xor(lt(x, 0), lt(x, 3))";
			break;
		case Operator::implies:
			s="implies(lt(x, 0), lt(x, 3))";
			break;
		case Operator::gcd:
			s="gcd(x, 3)";
			break;
		case Operator::lcm:
			s="lcm(x, 4)";
			break;
		case Operator::root:
			s="root(x, 2)";
			break;
		case Operator::selector:
			s="piecewise { gt(x,0) ? selector(1, vector{x, 1/x}), ? selector(2, vector{x, 1/x} ) }";
			break;
		case Operator::sum:
			s="x*sum(t : t=0..3)";
			break;
		case Operator::product:
			s="product(t : t=1..3)";
			break;
		case Operator::card:
			s="card(vector{x,y,z})";
			break;
		case Operator::scalarproduct:
			s="scalarproduct(vector{0,x}, vector{x,0})";
			break;
		case Operator::diff:
			s="diff(x^2 : x)";
			break;
		case Operator::factorial:
		case Operator::arcsech:
		case Operator::arcsec:
		case Operator::arccsch:
		case Operator::arccsc:
// 		case Operator::arccoth:
		case Operator::sin:
		case Operator::cos:
		case Operator::tan:
		case Operator::sec:
		case Operator::csc:
		case Operator::cot:
		case Operator::sinh:
		case Operator::cosh:
		case Operator::tanh:
		case Operator::sech:
		case Operator::csch:
		case Operator::coth:
		case Operator::arcsin:
		case Operator::arccos:
		case Operator::arctan:
		case Operator::arccot:
		case Operator::arcsinh:
		case Operator::arccosh:
// 		case Operator::arccsc:
// 		case Operator::arccsch:
// 		case Operator::arcsec:
// 		case Operator::arcsech:
		case Operator::arctanh:
		case Operator::exp:
		case Operator::ln:
		case Operator::log:
		case Operator::abs:
		//case Object::conjugate:
		//case Object::arg:
		//case Object::real:
		//case Object::imaginary:
		case Operator::floor:
		case Operator::ceiling:
		case Operator::_not:
			s=QString("%1(x)").arg(o.toString());
			break;
		case Operator::nOfOps:
		case Operator::none:
		case Operator::function:
// 		case Operator::real:
// 		case Operator::conjugate:
// 		case Operator::arg:
// 		case Operator::imaginary:
			break;
	}
	return s;
}

/*QString OperatorsModel::operToString(const Operator& op) const
{
	QStandardItem *it;
	
	for(int i=0; i<KEYWORDNUM; i++) {
		it=item(i,2);
		if(it!=NULL && it->data(Qt::EditRole).toInt()==op.operatorType()) {
			return item(i,0)->data(Qt::EditRole).toString();
}
}
	return QString();
}*/


